/**
 * @file icon-button
 * @author liaoxuezhi@baidu.com
 */
/* eslint fecs-indent: [0, "space", 2, 2] */

import * as React from 'react';
import {
  PureComponent,
  PropTypes
} from 'react';
import {Renderer, renderChildren, filter} from '../renderer';
import * as assign from 'object-assign';
import * as cx from 'classnames';
import * as schema from './service-schema.json';
import * as actions from './actions';
import {push, goBack} from 'react-router-redux';
import {sendNotify} from '../../actions/asist';
import onExpression from '../hoc/on-expression';
import reducer from './reducer';
import * as omit from 'lodash/omit';
import {buildSchemaApi} from '../util';


@Renderer({
  name: 'service',
  desc: '...',
  test: (path, config) => /(?:^|\/)service$/.test(path)
  || /(?:^|\/)form-item$/.test(path) && config.type === 'service',
  reducer,
  schema
}, null, assign({
  push,
  goBack,
  sendNotify
}, actions))
@onExpression
export default class ServiceRenderer extends PureComponent {
  static propTypes = {
    className: PropTypes.string,
    body: PropTypes.any,
    data: PropTypes.any,
    locals: PropTypes.any,
    api: PropTypes.any,
    schemaApi: PropTypes.any,
    resetData: PropTypes.func.isRequired
  };

  static defaultProps = {
    className: '',
    initFetch: true,
    messages: {},
    persist: false
  };

  static contextTypes = {
    scoped: PropTypes.object.isRequired
  };

  fetching = false;

  constructor(props) {
    super(props);

    this.state = {
      schema: this.props.body,
      data: assign({}, props.data)
    };

    this.reload = this.reload.bind(this);
    this.receive = this.receive.bind(this);
  }

  componentWillMount() {
    this.context.scoped.registerComponent(this);

    if (!this.props.persist) {
      this.props.resetData(this.props.scopedKey);
    }
  }

  componentDidMount() {
    const {
      initFetch,
      api,
      schemaApi,
      scopedKey,
      locals,
      fetchSchema
    } = this.props;

    if (initFetch && !schemaApi) {
      this.reload();
    } else if (schemaApi) {
      fetchSchema(scopedKey, buildSchemaApi(schemaApi), this.state.data, locals);
    }
  }


  componentWillReceiveProps(nextProps) {
    const props = this.props;
    const state = nextProps.state;
    const sendNotify = nextProps.sendNotify;
    const messages = nextProps.messages;
    const initFetch = nextProps.initFetch;
    const action = this.state.action;

    // 注意， assign 的顺序不一样。
    if (props.state.data !== state.data) {
      const data = assign({}, this.state.data, state.data);
      this.setState({
        data
      });
    } else if (props.data !== nextProps.data) {
      const data = assign({}, state.data, nextProps.data);
      this.setState({
        data
      });
    }

    // 提示获取数据情况
    if (props.state.fetching && !nextProps.state.fetching) {
      if (state.invalid) {
        let msg = messages.fetchFailed || (state.error instanceof Error ? state.error.message : state.message) || '获取数据失败';
        sendNotify({
          message: msg,
          level: 'error'
        });
      } else if (messages.fetchSuccess) {
        sendNotify({
          message: messages.fetchSuccess,
          level: 'success'
        });
      }
      this.fetching = false;
    }

    // action 提示
    if (props.state.action.saving && !nextProps.state.action.saving) {
      const actionState = state.action;

      if (actionState.invalid) {
        sendNotify({
          message: action.messages && action.messages.failed || (actionState.error instanceof Error ? actionState.error.message : actionState.message) || '操作失败',
          level: 'error'
        });
      } else {
        sendNotify({
          message: action.messages && action.messages.success || actionState.message || '操作成功',
          level: 'success'
        });

        action.reload && this.context.scoped.reload(action.reload);
      }
    }


    // schemaApi相关
    if (props.state.schema.fetching && !nextProps.state.schema.fetching) {
      const schema = nextProps.state.schema;

      if (schema.invalid) {
        let msg = (schema.error instanceof Error ? schema.error.message : schema.message) || '获取动态 Schema 失败';
        nextProps.sendNotify({
          message: msg,
          level: 'error'
        });
      } else {
        
        this.setState({
          schema: schema.data,
        }, initFetch && this.reload);
      }
    }
  }

  componentDidUpdate(prevProps, prevState) {
    const {
      api,
      fetchData,
      scopedKey,
      locals
    } = this.props;

    if (
      !this.fetching &&
      typeof api === 'string'
      && ~api.indexOf('$')
      && this.state.data !== prevState.data
      && filter(api, prevState.data) !== filter(api, this.state.data)
    ) {
      this.fetching = true;
      fetchData(scopedKey, api, this.state.data, locals);
    }
  }

  componentWillUnmount() {
    this.context.scoped.unRegisterComponent(this);
  }

  reload() {
    const {scopedKey, api, fetchData, locals} = this.props;
    const {data} = this.state;

    if (api && !this.fetching) {
      this.fetching = true;
      fetchData(scopedKey, api, data, locals);
    }
  }

  receive(data) {
    const {
      schemaApi,
      fetchSchema,
      scopedKey,
      locals
    } = this.props;
    this.setState({
      data: assign({}, this.state.data, data)
    }, () => schemaApi ? fetchSchema(scopedKey, buildSchemaApi(schemaApi), this.state.data, locals) : this.reload());
  }

  render() {
    const {
      className,
      $path,
      state
    } = this.props;

    const schema = this.state.schema;

    const loading = state.fetching || state.action.saving;
    const childProps = assign({}, omit(this.props, ['$path', 'body', 'className', 'api', 'schemaApi', 'initFetch', 'messages', 'type', 'name']), {
      data: this.state.data,
      locals: assign({}, this.props.locals, this.state.data, {
        $loading: loading
      })
    });

    return (
      <div
        className={cx('service', className)}
      >
        {schema ? renderChildren($path, schema, childProps) : null}
        {loading && (
          <div className="service-spinner">
            <i className="fa fa-spinner fa-spin fa-3x fa-fw text-white" />
          </div>
        )}
      </div>
    );
  }
}
